home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / quicktime / goodies / aiffwriter / aiffwriter.c < prev    next >
Encoding:
Text File  |  2000-06-23  |  17.0 KB  |  518 lines

  1. //////////
  2. //
  3. //    File:        AIFFWriter.c
  4. //
  5. //    Contains:    Code for dispatching a sound hardware output ('sdev') component.
  6. //
  7. //    Written by:    Mark Cookson, Apple Developer Technical Support
  8. //                Based on code by Kip Olson.
  9. //    Revised by: Tim Monroe
  10. //
  11. //    Copyright:    © 1993-1999 by Apple Computer, Inc., all rights reserved.
  12. //
  13. //    Change History (most recent first):
  14. //
  15. //       <2>         03/05/99    rtm        further work to bring into line with existing component sample code
  16. //       <1>         02/25/99    rtm        first inherited file; changes to support Windows compiles
  17. //
  18. //    You may incorporate this sample code into your applications without
  19. //    restriction, though the sample code has been provided "AS IS" and the
  20. //    responsibility for its operation is 100% yours. However, what you are
  21. //    not permitted to do is to redistribute the source as "Apple Sample
  22. //    Code" after having made changes. If you're going to re-distribute the
  23. //    source, we require that you make it clear in the source that the code
  24. //    was descended from Apple Sample Code, but that you've made changes.
  25. //
  26. //////////
  27.  
  28.  
  29. //////////
  30. //
  31. // header files
  32. //
  33. //////////
  34.  
  35. #include "AIFFWriter.h"
  36.  
  37.  
  38. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  39. //
  40. // Component dispatch helper defines
  41. //
  42. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  43.  
  44. #if TARGET_CPU_68K
  45.     #define COMPONENT_C_DISPATCHER
  46.     #define COMPONENT_DISPATCH_MAIN
  47. #endif
  48.  
  49. #define SOUNDCOMPONENT_BASENAME()         __SoundOutput
  50. #define SOUNDCOMPONENT_GLOBALS()         SoundOutputGlobalsPtr storage
  51.  
  52. #define CALLCOMPONENT_BASENAME()         SOUNDCOMPONENT_BASENAME()
  53. #define CALLCOMPONENT_GLOBALS()         SOUNDCOMPONENT_GLOBALS()
  54.  
  55. #define COMPONENT_UPP_SELECT_ROOT()        SoundComponent
  56. #define COMPONENT_DISPATCH_FILE            "SoundOutputDispatch.h"
  57.  
  58. #define    GET_DELEGATE_COMPONENT()        (storage->sourceComponent)
  59.  
  60. #include "Components.k.h"
  61. #include "Sound.k.h"
  62. #include "ComponentDispatchHelper.c"
  63.  
  64.  
  65. // It is possible to debug this component using an application. To do this, you cannot call the Time
  66. // Manager to generate interrupts, since source level debuggers are not re-entrant. This means you must call
  67. // the FakeInterrupt routine described below during main event loop time and define the gGlobals variable.
  68.  
  69. #ifdef FakeInterrupts
  70. SoundOutputGlobalsPtr                    gGlobals;
  71. #endif
  72.  
  73.  
  74. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  75. //
  76. // Required component calls
  77. //
  78. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  79.  
  80. /*    SoundOutputOpen
  81.  
  82.     This routine is called when the Component Manager creates an instance of this
  83.     component. The routine should allocate global variables in the appropriate heap
  84.     and call SetComponentInstanceStorage() so the Component Manager can remember
  85.     the globals and pass them to all the method calls.
  86.  
  87.     Determining the heap to use can be tricky. The Component Manager will normally
  88.     load the component code into the system heap, which is good, since many applications
  89.     will be sharing this component to play sound. In this case, the component's global
  90.     variable storage should also be created in the system heap.
  91.  
  92.     However, if system heap memory is tight, the Component Manager will load
  93.     the component into the application heap of the first application that plays sound.
  94.     When this happens, the component should create global storage in the application heap
  95.     instead. The Sound Manager will make sure that other applications will not try
  96.     to play sound while the component is in this application heap.
  97.  
  98.     To determine the proper heap to use, call GetComponentInstanceA5(). If the value
  99.     returned is 0, then the component was loaded into the system heap, and all storage
  100.     should be allocated there. If the value returned is non-zero, the component is in
  101.     the application heap specified by returned A5 value, and all storage should be
  102.     allocated in this application heap.
  103.  
  104.     NOTE: If the component is loaded into the application heap, the value returned by
  105.     GetComponentRefCon() will be 0.
  106.     NOTE: Do not attempt to initialize or access hardware in this call, since the
  107.     Component Manager will call SoundOutputOpen() BEFORE calling RegisterComponent().
  108.     Instead, initialize the hardware during InitOutputDevice(), described below.
  109.     NOTE: This routine is never called at interrupt time.
  110. */
  111.  
  112. PASCAL_RTN ComponentResult __SoundOutputOpen (SoundOutputGlobalsPtr globals, ComponentInstance self)
  113. {
  114.     OSErr        result;
  115.  
  116.     // get space for globals in appropriate heap
  117.     globals = (SoundOutputGlobalsPtr)NewPtrSysClear(sizeof(SoundOutputGlobals));
  118.     FailWithAction(globals == nil, result = MemError(), Failure);
  119.  
  120.     // If a component is loaded into an application's heap, then it gets an A5 value.
  121.     // This also means that the component's refCon is 0. All of this means we need
  122.     // to go and get the preferences just like we're being registered for the first time.
  123.     
  124.     if (GetComponentInstanceA5(self) != 0)
  125.     {
  126.         globals->inAppHeap = true;
  127.         result = GetPreferences(globals);
  128.         FailIf(result != noErr, NoPrefs);
  129.     }
  130.  
  131.     globals->self = self;
  132.     globals->prefs = (HardwarePrefsHandle)GetComponentRefcon((Component)self);
  133.  
  134.  
  135. #ifdef FakeInterrupts
  136.     gGlobals = globals;
  137. #endif
  138.  
  139.     SetComponentInstanceStorage(self, (Handle) globals);     // save pointer to our globals
  140.     return (noErr);
  141.  
  142. NoPrefs:
  143.     DisposePtr((Ptr)globals);
  144. Failure:
  145.     return (result);
  146. }
  147.  
  148.  
  149. /*    SoundOutputClose
  150.  
  151.     This routine is called when the Component Manager is closing the instance of
  152.     this component. The routine should make sure all remaining data is written
  153.     to the hardware and that the hardware is completely turned off. It should
  154.     delete all global storage and close any other components that were opened.
  155.  
  156.     NOTE: Be sure to check that the globals pointer passed in to this routine is
  157.     not set to NIL. If the SoundOutputOpen() routine fails for any reason, the Component
  158.     Manager will call this routine passing in a NIL for the globals.
  159.     NOTE: This routine is never called at interrupt time.
  160. */
  161.  
  162. PASCAL_RTN ComponentResult __SoundOutputClose (SoundOutputGlobalsPtr globals, ComponentInstance self)
  163. {
  164.     if (globals != nil)                                            // we have some globals
  165.     {
  166.         ReleaseHardware(globals);                                // make sure the hardware is off and release it
  167.  
  168.         if (globals->sourceComponent)
  169.             CloseMixerSoundComponent(globals->sourceComponent);    // close mixer
  170.  
  171.         if (globals->prefsChanged)
  172.             ErrorMessage(SetSoundPreference(kAIFFWriterSubType, kAIFFWriterName, (Handle)globals->prefs));
  173.  
  174.         if (globals->inAppHeap)
  175.             DisposeHandle((Handle)globals->prefs);
  176.  
  177.         if (globals->ioBuffers[0].buffer != nil)
  178.             DisposePtr(globals->ioBuffers[0].buffer);            // dispose of buffers
  179.         if (globals->ioBuffers[1].buffer != nil)
  180.             DisposePtr(globals->ioBuffers[1].buffer);            // dispose of buffers
  181.  
  182.         DisposePtr((Ptr)globals);                                // torch our storage
  183.     }
  184.     return (noErr);
  185. }
  186.  
  187.  
  188. /*    SoundOutputRegister
  189.  
  190.     This routine is called once, usually at boot time, when the Component Manager
  191.     is first registering this component. This routine should check to see if the proper
  192.     hardware is installed and return 0 if it is. If the hardware is not installed,
  193.     the routine should return 1 and this component will not be registered. This is
  194.     also an opportunity to do one-time initializations and perhaps register this
  195.     component again if more than one hardware device is available. Global state information
  196.     can also be saved in the component refcon by calling SetComponentRefCon();
  197.  
  198.     NOTE: The cmpWantsRegisterMessage bit must be set in the component flags of the
  199.     component in order for this routine to be called.
  200.     NOTE: This routine is never called at interrupt time.
  201. */
  202.  
  203. PASCAL_RTN ComponentResult __SoundOutputRegister (SoundOutputGlobalsPtr globals)
  204. {
  205.     NumVersion            installedVersion;
  206.     ComponentResult        result;
  207.     OSErr                err;
  208.  
  209.     // we can only run if version 3.0 or greater of the Sound Manager is running;
  210.     // we can check the entire long because the format of NumVersion is BCD data
  211.     installedVersion = SndSoundManagerVersion();
  212.     if (installedVersion.majorRev < 3)
  213.         result = 1;                                    // component doesn't want to be registered
  214.     else
  215.     {
  216.         result = 0;                                    // component wants to be registered
  217.         err = GetPreferences(globals);
  218.         FailWithAction(err != noErr, result = 1, Exit);
  219.     }
  220.  
  221. Exit:
  222.     return result;
  223. }
  224.  
  225.  
  226. PASCAL_RTN ComponentResult __SoundOutputVersion (SoundOutputGlobalsPtr globals)
  227. {
  228.     globals; // suppress "unused variable" warning for all compilers
  229.  
  230.     return (kSoundComponentVersion);
  231. }
  232.  
  233.  
  234. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  235. //
  236. // Sound component functions
  237. //
  238. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  239.  
  240. /*    InitOutputDevice
  241.  
  242.     This routine is called once when the Sound Manager first opens this component.
  243.     The routine should initialize the hardware to default values, allocate the
  244.     appropriate mixer component and create any other memory that is required.
  245.  
  246.     NOTE: This routine is never called at interrupt time.
  247. */
  248.  
  249. PASCAL_RTN ComponentResult __SoundOutputInitOutputDevice (SoundOutputGlobalsPtr globals, long actions)
  250. {
  251.     ComponentResult        result;
  252.  
  253.     actions;            // suppress "unused variable" warning for all compilers
  254.     
  255.     result = SetupHardware(globals);
  256.     FailIf(result != noErr, Failure);
  257.  
  258.     // if any of the preferences are needed during the interrupts,
  259.     // then locked the preferences handle now
  260.  
  261.     // first create a mixer and tell it the type of data it should output. The
  262.     // description includes sample format, sample rate, sample size, number of channels
  263.     // and the size of your optimal interrupt buffer. If a mixer cannot be found that
  264.     // will output this type of data, an error will be returned.
  265.  
  266.     result = OpenMixerSoundComponent(&globals->outputData, 0, &globals->sourceComponent);
  267.     FailIf(result != noErr, Failure);
  268.     return (noErr);
  269.  
  270. Failure:
  271.     return (result);
  272. }
  273.  
  274.  
  275. /*    SoundOutputGetInfo
  276.  
  277.     This routine returns information about this component to the Sound Manager. A
  278.     4-byte OSType selector is used to determine the type and size of the information
  279.     to return. If the component does not support a selector, it should delegate this
  280.     call on up the component chain.
  281.  
  282.     NOTE: This can be called at interrupt time. However, selectors that return
  283.     a handle will not be called at interrupt time.
  284. */
  285.  
  286. PASCAL_RTN ComponentResult __SoundOutputGetInfo (SoundOutputGlobalsPtr globals, SoundSource sourceID, OSType selector, void *infoPtr)
  287. {
  288.     SoundInfoListPtr    listPtr;
  289.     UnsignedFixed        *lp;
  290.     Handle                infoHandle;
  291.     ComponentResult        result;
  292.     short                *sp;
  293.  
  294.     result = noErr;
  295.     switch (selector)
  296.     {
  297.         case siSampleSize:                                // return current sample size
  298.             *((short *) infoPtr) = (**globals->prefs).sampleSize;
  299.             break;
  300.  
  301.         case siSampleSizeAvailable:                        // return samples sizes available
  302.             infoHandle = NewHandle(sizeof(short) * 2);    // space for sample sizes
  303.             FailWithAction(infoHandle == nil, result = MemError(), Exit);
  304.  
  305.             listPtr = (SoundInfoListPtr)infoPtr;
  306.             listPtr->count = 2;                            // no. sample sizes in handle
  307.             listPtr->infoHandle = infoHandle;            // handle to be returned
  308.  
  309.             sp = (short *) *infoHandle;                    // store sample sizes in handle
  310.             *sp++ = 8;
  311.             *sp++ = 16;
  312.             break;
  313.  
  314.         case siSampleRate:                                // return current sample rate
  315.             *((Fixed *) infoPtr) = (**globals->prefs).sampleRate;
  316.             break;
  317.  
  318.         case siSampleRateAvailable:                        // return sample rates available
  319.             infoHandle = NewHandle(sizeof(UnsignedFixed) * 5);    // space for sample rates
  320.             FailWithAction(infoHandle == nil, result = MemError(), Exit);
  321.  
  322.             listPtr = (SoundInfoListPtr)infoPtr;
  323.             listPtr->count = 5;                            // no. sample rates in handle
  324.             listPtr->infoHandle = infoHandle;            // handle to be returned
  325.  
  326.             // If the hardware supports a limited set of sample rates, then the list count
  327.             // should be set to the number of sample rates and this list of rates should be
  328.             // stored in the handle.
  329.             lp = (UnsignedFixed *) *infoHandle;
  330.             *lp++ = rate48khz;
  331.             *lp++ = rate44khz;
  332.             *lp++ = rate22050hz;
  333.             *lp++ = rate11025hz;
  334.             *lp++ = 0x1F400000;                            // 8kHz sample rate not defined
  335.             break;
  336.  
  337.         case siNumberChannels:                            // return current no. channels
  338.             *((short *) infoPtr) = (**globals->prefs).numChannels;
  339.             break;
  340.  
  341.         case siChannelAvailable:                        // return channels available
  342.             infoHandle = NewHandle(sizeof(short) * 2);    // space for channels
  343.             FailWithAction(infoHandle == nil, result = MemError(), Exit);
  344.  
  345.             listPtr = (SoundInfoListPtr)infoPtr;
  346.             listPtr->count = 2;                            // no. channels in handle
  347.             listPtr->infoHandle = infoHandle;            // handle to be returned
  348.  
  349.             sp = (short *) *infoHandle;                    // store channels in handle
  350.             *sp++ = 1;                                    // mono
  351.             *sp++ = 2;                                    // stereo
  352.             break;
  353.  
  354.         case siHardwareVolume:
  355.             *((long *)infoPtr) = (**globals->prefs).volume;
  356.             break;
  357.  
  358.         case siHardwareVolumeSteps:
  359.             *((short *) infoPtr) = kHardwareVolumeSteps;
  360.             break;
  361.  
  362.         case siHardwareMute:
  363.             *((short *) infoPtr) = (**globals->prefs).muteState;
  364.             break;
  365.             
  366.         case siHardwareBusy:
  367.             *((short *)infoPtr) = globals->hardwareOn;
  368.             break;
  369.  
  370.         // if you do not handle this selector, then delegate it up the chain
  371.         default:
  372.             result = SoundComponentGetInfo(globals->sourceComponent, sourceID, selector, infoPtr);
  373.             break;
  374.     }
  375.  
  376. Exit:
  377.     return (result);
  378. }
  379.  
  380.  
  381. /*    SoundOutputSetInfo
  382.  
  383.     This routine sets information about this component. A 4-byte OSType selector is
  384.     used to determine the type and size of the information to apply. If the component
  385.     does not support a selector, it should delegate this call on up the component chain.
  386.  
  387.     NOTE: This can be called at interrupt time.
  388. */
  389.  
  390. PASCAL_RTN ComponentResult __SoundOutputSetInfo (SoundOutputGlobalsPtr globals, SoundSource sourceID, OSType selector, void *infoPtr)
  391. {
  392.     ComponentResult            result;
  393.  
  394.     result = noErr;
  395.     switch (selector)
  396.     {
  397.         case siSampleSize:                                // set sample size
  398.             switch ((short)infoPtr)
  399.             {
  400.                 case 8:                                    // valid sample sizes
  401.                     (**globals->prefs).sampleSize = (short)infoPtr;
  402.                     globals->outputData.format = k8BitOffsetBinaryFormat;
  403.                     globals->prefsChanged = true;
  404.                     break;                
  405.                 
  406.                 case 16:
  407.                     (**globals->prefs).sampleSize = (short)infoPtr;
  408.                     globals->outputData.format = k16BitBigEndianFormat;
  409.                     globals->prefsChanged = true;
  410.                     break;
  411.                 
  412.                 default:
  413.                     result = siInvalidSampleSize;
  414.             }
  415.             break;
  416.  
  417.         case siSampleRate:                                // set sample rate
  418.             switch ((UnsignedFixed)infoPtr)
  419.             {
  420.                 case rate48khz:                            // valid sample rates
  421.                 case rate44khz:
  422.                 case rate22050hz:
  423.                 case rate11025hz:
  424.                 case 0x1F400000:
  425.                     (**globals->prefs).sampleRate = (UnsignedFixed)infoPtr;
  426.                     globals->prefsChanged = true;
  427.                     break;
  428.                 
  429.                 default:
  430.                     result = siInvalidSampleRate;
  431.             }
  432.             break;
  433.  
  434.         case siNumberChannels:                            // set no. channels
  435.             if ( ((short)infoPtr == 1) || ((short)infoPtr == 2) )
  436.             {
  437.                 (**globals->prefs).numChannels = (short)infoPtr;
  438.                 globals->prefsChanged = true;
  439.             }
  440.             else
  441.                 result = notEnoughHardware;
  442.             break;
  443.  
  444.         case siHardwareVolume:
  445.             // the volume is two 16 bit values combined into a long
  446.             // the range is 0x0000 - 0x0100, where 0x0100 is the max volume level
  447.             (**globals->prefs).volume = (long)infoPtr;
  448.             globals->prefsChanged = true;
  449.             break;
  450.  
  451.         case siHardwareMute:
  452.             (**globals->prefs).muteState = (short)infoPtr;
  453.             globals->prefsChanged = true;
  454.             break;
  455.             
  456.         // if you do not handle this selector, then call up the chain
  457.         default:
  458.             result = SoundComponentSetInfo(globals->sourceComponent, sourceID, selector, infoPtr);
  459.             break;
  460.     }
  461.     return (result);
  462. }
  463.  
  464.  
  465. /*    StartSource
  466.  
  467.     This routine is used to start sounds playing that are currently paused. It should
  468.     first delegate this call up the component chain so the rest of the chain can prepare
  469.     to play this sound. Then, if the hardware is not already started it should be
  470.     turned on.
  471.  
  472.     NOTE: This can be called at interrupt time.
  473. */
  474.  
  475. PASCAL_RTN ComponentResult __SoundOutputStartSource (SoundOutputGlobalsPtr globals, short count, SoundSource *sources)
  476. {
  477.     ComponentResult        result;
  478.  
  479.     // tell the mixer to start these sources
  480.     result = SoundComponentStartSource(globals->sourceComponent, count, sources);
  481.     FailIf(result != noErr, Exit);
  482.  
  483.     // make sure hardware interrupts are running
  484.     StartHardware(globals);
  485.  
  486. Exit:
  487.     return (result);
  488. }
  489.  
  490.  
  491. /*    PlaySourceBuffer
  492.  
  493.     This routine is used to specify a new sound to play and conditionally start
  494.     the hardware playing that sound. It should first delegate this call up the component
  495.     chain so the rest of the chain can prepare to play this sound. Then, if the
  496.     hardware is not already started it should be turned on.
  497.  
  498.     NOTE: This can be called at interrupt time.  */
  499.  
  500. PASCAL_RTN ComponentResult __SoundOutputPlaySourceBuffer (SoundOutputGlobalsPtr globals, SoundSource sourceID, SoundParamBlockPtr pb, long actions)
  501. {
  502.     ComponentResult        result;
  503.  
  504.     // tell mixer to we'll start playing this new buffer
  505.     result = SoundComponentPlaySourceBuffer(globals->sourceComponent, sourceID, pb, actions);
  506.     FailIf(result != noErr, Exit);
  507.  
  508.     // if the kSourcePaused bit is set, then do not turn on your hardware just yet
  509.     // (the assumption is that StartSource() will later be used to start this sound playing).
  510.     // If this bit is not set, turn your hardware interrupts on.
  511.  
  512.     if ( !(actions & kSourcePaused) )
  513.         StartHardware(globals);
  514.  
  515. Exit:
  516.     return (result);
  517. }
  518.